﻿using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace MicroMVVM.Navigation
{
    /// <summary>
    /// This class represents the Navigation functionality.
    /// </summary>
    public static class Navigation
    {
        /// <summary>
        /// Navigates from a Page to the parametrised view passing specified parameter.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="view"></param>
        /// <returns></returns>
        public static bool Navigate<T>(this T source, Type view) where T : class
        {
            return Navigate(source, view, null, false);
        }

        /// <summary>
        /// Navigates from a Page to the parametrised view passing specified parameter.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="view"></param>
        /// <param name="parameter"></param>
        /// <param name="serializeParameter"></param>
        /// <returns></returns>
        public static bool Navigate<T>(this T source, Type view, object parameter, bool serializeParameter = true)
            where T : class
        {
            var application = source as Application;

            if (application != null)
                return Navigate(view, parameter, serializeParameter);

            var page = source as Page;

            if (page != null)
                return Navigate(page, view, parameter, serializeParameter);

            var userControl = source as UserControl;

            if (userControl != null)
                return Navigate(userControl, view, parameter, serializeParameter);

            var iNavigation = source as INavigation;

            if (iNavigation != null)
                return Navigate(iNavigation, view, parameter, serializeParameter);

            return true;
        }

        /// <summary>
        /// Navigates from a UserControl to the parametrised view passing specified parameter.
        /// </summary>
        /// <param name="userControl"></param>
        /// <param name="view"></param>
        /// <param name="parameter"></param>
        /// <param name="serializeParameter"></param>
        /// <returns></returns>
        private static bool Navigate(UserControl userControl, Type view, object parameter, bool serializeParameter = true)
        {
            var page = NavigationHelpers.GetParent<Page>(userControl) as Page;

            if (page == null) return false;

            var iNavigation = page.DataContext as INavigation;

            return iNavigation != null ? Navigate(iNavigation, view, parameter, serializeParameter) : Navigate(page, view, parameter, serializeParameter);
        }

        /// <summary>
        /// Navigates from a Page to the parametrised view passing specified parameter.
        /// </summary>
        /// <param name="page"></param>
        /// <param name="view"></param>
        /// <param name="parameter"></param>
        /// <param name="serializeParameter">Indicates whether to serialize the parameter</param>
        /// <returns></returns>
        private static bool Navigate(Page page, Type view, object parameter, bool serializeParameter = true)
        {
            if (page == null) return false;

            var frame = page.Frame;

            if (frame != null)
            {
                return parameter == null
                           ? frame.Navigate(view)
                           : frame.Navigate(view, serializeParameter ? Serializer.Serialize(parameter) : parameter);
            }

            return false;
        }

        /// <summary>
        /// Navigates from App.XAML to the parametrised view passing specified parameter.
        /// </summary>
        /// <param name="view"></param>
        /// <param name="parameter"></param>
        /// <param name="serializeParameter">Indicates whether to serialize the parameter</param>
        /// <returns></returns>
        private static bool Navigate(Type view, object parameter, bool serializeParameter = true)
        {
            var frame = Window.Current.Content as Frame;

            if (frame == null) return false;

            var element = frame as DependencyObject;

            element.SetValue(Navigator.IsEnabledProperty, true);

            return parameter == null
                       ? frame.Navigate(view)
                       : frame.Navigate(view, serializeParameter ? Serializer.Serialize(parameter) : parameter);
        }

        /// <summary>
        /// Navigates from an INavigation implementation to the parametrised view passing specified parameter.
        /// </summary>
        /// <param name="viewModel">INavigation ViewModel</param>
        /// <param name="view">View type</param>
        /// <param name="parameter">Parameter</param>
        /// <param name="serializeParameter">Indicates whether to serialize the parameter</param>
        /// <returns>True/False</returns>
        private static bool Navigate(INavigation viewModel, Type view, object parameter, bool serializeParameter = true)
        {
            if (viewModel == null) return false;

            if (viewModel.Current == null) return false;

            return parameter == null
                       ? viewModel.Current.Navigate(view)
                       : viewModel.Current.Navigate(view, serializeParameter ? Serializer.Serialize(parameter) : parameter);
        }

        /// <summary>
        /// Deserialises the parametrised object.
        /// </summary>
        /// <typeparam name="T">Object type</typeparam>
        /// <param name="viewModel">INavigation ViewModel</param>
        /// <param name="serializedData">Serialised serializedData</param>
        /// <returns>Instance of the deserialised object</returns>
        public static T GetParametrizedData<T>(this INavigation viewModel, object serializedData)
        {
            var data = serializedData as string;

            return string.IsNullOrEmpty(data) ? default(T) : Serializer.Deserialize<T>(data);
        }
    }
}